*> 100 Cool Things - http://cobolrocks.codeplex.com/
*> Copyright (c) 2009
*> by Emerson Lopes (emersonlopes@gmail.com)
*>
*> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 
*> documentation files (the "Software"), to deal in the Software without restriction, including without limitation 
*> the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and 
*> to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*>
*> The above copyright notice and this permission notice shall be included in all copies or substantial portions 
*> of the Software.
*>
*> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 
*> TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
*> THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 
*> CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
*> DEALINGS IN THE SOFTWARE.

CLASS-ID. OrderProcessing AS "AdapterPattern.OrderProcessing".

  environment division.
    configuration section.
        repository.
            interface ICreditCardAdapter     as "AdapterPattern.ICreditCardAdapter"
            class ClassCustomer              as "AdapterPattern.Customer"
            class ClassPurchaseDetails       as "AdapterPattern.PurchaseDetails"
            class ClassLegacyVisa            as "Legacy.VisaPaymentMethod"
            class ClassLegacyAmex            as "Legacy.AmexPaymentMethod"
            class ClassProduct               as "AdapterPattern.Product"
            class SystemString               as "System.String"
            Class SystemObject               as "System.Object"
            Class SystemType                 as "System.Type".

 *> Instance's data and methods
    object.
        data division.
            working-storage section.
            
            copy "types.book".

            01 purchaseNumber      type SystemInt32 value 1.
            01 aType               usage object reference SystemType.
            01 aCustomer           usage object reference ClassCustomer.
            01 visaLegacyCasted    usage object reference ClassLegacyVisa.
            01 amexLegacyCasted    usage object reference ClassLegacyAmex.
            01 creditCardType      usage object reference SystemObject.
            
            01 creditCardName      usage object reference SystemString.
            01 creditCardNumber    usage object reference SystemString.
            01 securityCode        usage object reference SystemString.
            01 amount              type SystemDouble.

            01 creditCardAdapter   usage object reference ICreditCardAdapter.
            
        procedure division.

             *> ... lots of methods
             
             method-id. ProcessOrdersLegacy  as "ProcessOrderLegacy".
                 data division.
                     working-storage section.
                     01 aPurchase usage object reference ClassPurchaseDetails.
                     
                 procedure division.
                     
                     *> Here would be code to retrieve order from a DB
                     *> ...
                     
					 invoke ClassPurchaseDetails       "NEW" returning aPurchase
					 
					 invoke aPurchase "GetDetails" using purchaseNumber
					 
					 add    1                to  purchaseNumber
					 
					 set    aCustomer     to  aCustomer of aPurchase 

					 set creditCardType   to  paymentMethod of aPurchase
					 
					 *> Now we need to know which credit card the user is using
					 *> Since both legacy Visa and Amex classes has incompatible interfaces, a single 
					 *> method call would not be enough.
					 
					 invoke creditCardType "GetType" returning aType
					 
					 *> aType now contains the object type. 
					 
					 invoke aType "ToString" returning creditCardName.

                     if (SystemString::"Equals"(creditCardName, "Legacy.VisaPaymentMethod") = b"1") then
                         *> We now need to perform a cast to use the object
                         set    visaLegacyCasted    to   creditCardType  as ClassLegacyVisa

                         *> Some verbosity...alas
                         set  creditCardNumber      to  creditCardNumber of aCustomer
                         set  securityCode          to  securityCode     of aCustomer
                         set  amount                to  totalValue       of aPurchase
                         
                         invoke visaLegacyCasted "ProcessPayment" using creditCardNumber
                                                                        securityCode    
                                                                        amount     
                     end-if
                     
                     if (SystemString::"Equals"(creditCardName, "Legacy.AmexPaymentMethod") = b"1") then
                         *> Not only tedious, but also error prone
                         *> Code would not work  if type name is incorrect or even abend if casted type method name/parameters don't match...
                         set    amexLegacyCasted    to   creditCardType  as ClassLegacyAmex

                         *> Some verbosity...alas
                         set  creditCardNumber      to  creditCardNumber of aCustomer
                         set  securityCode          to  securityCode     of aCustomer
                         set  amount                to  totalValue       of aPurchase
                         
                         invoke amexLegacyCasted "ChargeCreditCard" using amount
                                                                          creditCardNumber
                                                                          securityCode    
                     end-if
                     *> If another credit card is going to be supported you would need to change this code 
                     *> We need a simple, unified method that could handle any credit card, both current and future
            end method ProcessOrdersLegacy.

            *>Enter the Adapter Pattern!
            method-id. ProcessOrdersWithAdapter  as "ProcessOrdersWithAdapter".
                 data division.
                     working-storage section.
                     01 aPurchase usage object reference ClassPurchaseDetails.
                     
                 procedure division.
                     
                     *> Here would be code to retrieve order from a DB
                     *> ...
                     
					 invoke ClassPurchaseDetails "NEW" returning aPurchase

 					 invoke aPurchase "GetDetailsAdapted" using 1 

					 set  aCustomer                 to  aCustomer of aPurchase
					 
                     set  creditCardNumber          to  creditCardNumber     of aCustomer
                     set  securityCode              to  securityCode         of aCustomer
                     set  amount                    to  totalValue           of aPurchase
                     
                     set  creditCardAdapter         to  paymentMethodAdapter of aPurchase  
                     
                     invoke creditCardAdapter "ProcessPaymentAdapter" using creditCardNumber
                                                                            securityCode    
                                                                            amount    
                                                                    
                     *> What a relief! Code is cleaner, understandable and smaller!
            end method ProcessOrdersWithAdapter.


    end object.
END CLASS OrderProcessing.

